# -*- coding: utf-8 -*-

import xml.dom.minidom as dom
from renyare.wsgi import RenyareApplication
import re

filters = {
'empty':'^$',
'alphanumeric':'^[a-z0-9A-Z]+$',
'sentence':'^[a-z0-9A-Z ]+$',
'alpha':'^[a-zA-Z]+$',
'numeric':'^[0-9]+$',
'password':'^[a-zA-Z0-9_@&]$',
'email':'^.*$',
'any':'.*'
}

XML = 0
XHTML = 1
XFORM = 2
_render = { 0:'_render_xml', 1:'_render_xhtml', 2:'_render_xform' }
XML_NS = None
XML_NSP = ''
XHTML_NS = None
XHTML_NSP = ''
XFORM_NS = 'http://www.w3.org/TR/xforms'

class DOMForm(object):
	"""
	Low-level form interface. Provides basic form validation and data processing functions
	"""
	tag = 'form'
	def __init__(self, schema, data, reset=False):
		"""
		Base class for form creation
		"""
		self._messages = []
		self.xml = dom.DocumentFragment(self.NS, 'form')
		self.xml_root = self.xml.documentElement

	def add_msg(self, text=None, id_=None, critical=False):
		"""
		Add a message to the form
		"""
		m = Message(text=text, id_=id_, critical=critical)
		self.messages.append(m)

	def _make_field(self, element, owner):
		"""
		Simplify form element creation.
		"""
		field = owner.createElementNS(None, element)
		if(element in ['text', 'button', 'hidden', 'radio', 'password', 'file']):
			field.tagName = 'input'
			field.setAttributeNS(None, 'type', element)
		else:
			field.tagName = element
		return field

	def reset_field(self, f_name):
		for f in self.form:
			if(f[0] == f_name):
				tmp = list(f)
				tmp[5] = self.defaults.get(f[0])
				self.form[self.form.index(f)] = tuple(tmp)

	def process_data(self, check_fields, fs=None, save_old=True):
		"""
		Check form after it's data has been taken from user
		"""
		if(not fs and not self.validated):
			return False
		if(not self.validated):
			self.validate(fs, fill_good=save_old)
		if(not check_fields(self.fields, self.bad, self.not_found)
			or self.bad
			or self.not_found):
			self._append_checks()
			return False
		return True

	def _append_checks(self):
		"""
		Appends a check tag to main node. Attributes of a tag are field names, values are 1 or 2
		1 means value is filled badly (not matched filter),
		2 means value is required, but was not found in field list or was not filled at all
		"""
		check_node = self.xml.createElementNS(None, 'check')
		self.form_node.appendChild(check_node)
		for b in self.bad:
			check_node.setAttribute(b, '1')
		for n in self.not_found:
			check_node.setAttribute(n, '2')
		return check_node

	def validate(self, fs, fill_good=True):
		"""
		Filter fields, got from user input.
		fs should be FieldStorage class instance from cgi module.
		"""
		if( not (
			fs
			and fs.has_key(self.submit[0])
			#and fs[self.submit[0]].value == self.submit[1]
			)
		):
			self.validated = False
			return False
		self.fields = {}
		for f in self.form:
			if(fs.has_key(f[0])):
				if(not fs[f[0]].value and f[3]):
					self.not_found.append(f[0])
				else:
					if(filters.has_key(f[2])):
						match = filters[f[2]]
					else:
						match = f[2]
					if(re.search(match, fs[f[0]].value)):
						self.fields[f[0]] = fs[f[0]].value
						if(fill_good):
							tmp = list(f)
							self.defaults[f[0]] = f[5]
							tmp[5] = fs[f[0]].value
							self.form[self.form.index(f)] = tuple(tmp)
					else:
						self.bad.append(f[0])
		self.validated = True
		return self.fields

	def render(self, output=XML):
		"""Render the form into the DOM tree"""
		return getattr(self, _render[output])()

	def _render_xml(self):
		"""
		Build a form in own XML format for further xslt processing
		"""
		form_node = self.xml.createElementNS(XML_NS, XML_NSP+'form')
		self.xml_root.appendChild(form_node)
		msg = self.xml.createElementNS(XML_NS, XML_NSP+'messages')
		form_node.appendChild(msg)
		if self.title:
			title = form_node.createElementNS(XML_NS, XML_NSP+'form')
			title.appendChild(self.xml.createTextNode(self.title))
		self.action and self.form_node.setAttribute('action', self.action)
		self.form_node.setAttribute('method', self.method.upper())
		if(self.submit):
			form = list(self.form)
			form.append( (self.submit[0], 'submit', self.submit[1], False, {}, self.submit[1]) )
		for el in form:
			field = self._make_field(el[1], self.xml)
			field.setAttributeNS(None, 'name', el[0])
			if(el[3]):
				field.setAttributeNS(None, 'required', '1')
			if(el[4]):
				for (k,v) in el[4].items():
					field.setAttributeNS(None, k, v)
			field.setAttributeNS(None, 'value', el[5])
			self.form_node.appendChild(field)

	def _render_xhtml(self):
		pass

	def _render_xform(self):
		"""
		Build a form in XForms format
		(Doesn't work yet)
		"""
		nsp = 'xforms:' # XForms namespace prefix
		model = parent or self.xml.createElementNS(xfNS, nsp+'model')
		subm = self.xml.createElementNS(None, nsp+'submission')
		self.action and subm.setAttribute('action', self.action)
		subm.setAttribute('method', self.req_method.lower())
		subm.setAttribute('id', 'f'+self.fName) # Won't work
		model.appendChild(subm)
		form = list(self.form)
		form.append( (self.submit[0], 'submit', self.submit[1]) )
		for i in form:
			elem = self.xml.createElementNS(None, nsp+i[1])
			elem.setAttribute('ref', self.fName+i[0]) # Won't work
			label = self.xml.createElementNS(None, nsp+'label')
			label.appendChild(self.xml.createTextNode(i[2]))
			elem.appendChild(label)
			model.appendChild(elem)
		self.xml_root.appendChild(model)
		return self.xml

class Field(object):
    def __init__(self, name, value, required):
        pass

class Submit(Field):
	def __init__(self, name, value, url=''):
		Field.__init__(self, name, value=value, required=True)
		self.filter = re.compile(name)
		self.url = url

class SimpleForm(RenyareApplication):
	"""
	WSGI application base class to make a simple form from it
	"""

	def call(self, environ, start_response):
		self.redraw = False
		self._process()
		headers = [('Content-type', 'application/xml')]
		status = '200 OK'
		start_response(status, headers)
		yield ''

	def _get_input(self):
		self.fs = None
		if(self.environ.get('wsgi.post_form')):
			self.fs = self.environ['wsgi.post_form'][2]

	def _process(self):
		"""
		Main processing function
		"""
		module_name = self.environ.get('renyare.application') or 'default_form'
		(self.xml, self.xml_root) = get_xml_root(self.environ, module_name)
		self._get_input()
		( form, submit, title, tag ) = self.init_form()
		self._form = FormMaker(form, submit, parent=self.xml_root, form_data=self.fs, title=title, tag=tag)
		if(not self._form.validated): # Start case - no input from user
			self._form.render()
		else:
			check = self._form.process_data(self.check_fields)
			if(not check): # There was input data, but not correct
				self._form.render()
				self.add_msg( _("Some fields was filled wrong or not filled at all") )
			else:
				self.form_ok(self._form.fields)
				self.ok = True
				if(self.redraw == True):
					for f in self._form.fields:
						self._form.reset_field(f)
					self._form.xhtmlForm()
#				except:
#					self.add_msg( _("Errors happened! Please, contact site administrator.") )

	def check_fields(self, fields, bad, nf):
		"""
		Can be overloaded in child class to make additional checks of form fields
		Should return True of check is passed
		"""
		raise NotImplementedError, "This is base class. It cannot be used directly"

	def form_ok():
		"""
		This defines things to do when all form checks are passed
		"""
		raise NotImplementedError, "This is base class. It cannot be used directly"

	def add_msg(self, msg, **kwargs):
		self._form.add_msg( msg, **kwargs )

class SequenceForm(SimpleForm):
	def init_form(self):
		"""
		MUST be overloaded in child classes - form sequence is defined here as
		self.forms = [form1, form2, ...]
		See documentation for how to build forms themselves
		"""
		self.forms = []
